﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using SkyShell.ViewModel;
using mshtml;
using System.Reflection;
using System.Globalization;
using System.Threading.Tasks;
using GalaSoft.MvvmLight.Threading;

namespace SkyShell
{
    /// <summary>
    /// Interaction logic for SkyShellAuthPage.xaml
    /// </summary>
    public partial class SkyShellAuthPage : Page
    {
        private ShareContentViewModel _viewModel;

        public SkyShellAuthPage()
        {
            InitializeComponent();
            LoginWebBrowser.Visibility = System.Windows.Visibility.Hidden;
            this.Loaded += new RoutedEventHandler(SkyShellAuthPage_Loaded);
            _viewModel = DataContext as ShareContentViewModel;
            _viewModel.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(_viewModel_PropertyChanged);
            LoginWebBrowser.LoadCompleted += new LoadCompletedEventHandler(LoginWebBrowser_LoadCompleted);
        }

        void _viewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "ShareLink")
            {
                LoginWebBrowser.Visibility = System.Windows.Visibility.Hidden;
                BusyBar.Visibility = System.Windows.Visibility.Visible;
                LoginWebBrowser.Navigate(_viewModel.ShareLink);
                Console.WriteLine(_viewModel.ShareLink);
            }
            if (e.PropertyName == "AuthUrl") 
            {
                LoginWebBrowser.Navigate(_viewModel.AuthUrl);
            }
        }

        void SkyShellAuthPage_Loaded(object sender, RoutedEventArgs e)
        {
            _viewModel.Load();
        }


        /// <summary>
        /// Checks whether the current site is login window
        /// </summary>
        /// <param name="uri"></param>
        /// <returns></returns>
        private bool DetectLoginWindow(Uri uri)
        {
            bool result = uri.LocalPath == "/login.srf" || uri.LocalPath == "/oauth20_authorize.srf";
            return result;
        }

        /// <summary>
        /// Checks whether the website is the Live app authorization site
        /// </summary>
        /// <param name="uri"></param>
        /// <returns></returns>
        private bool DetectAuthWindow(Uri uri)
        {
            bool result = uri.LocalPath == "/Consent/Update";
            return result;
        }


        bool _userAccepted = false;

        //for the API use case
        // void LoginWebBrowser_LoadCompleted(object sender, NavigationEventArgs e)
        // {
        //     if (_userAccepted)
        //     {
        //         _viewModel.AccessAccepted.Execute(e.Uri);
        //         return;
        //     }

        //     if (e.Uri.Host != "account.live.com")
        //         return;

        //     HTMLDocument doc = (HTMLDocument)this.LoginWebBrowser.Document;
        //     doc.documentElement.style.overflow = "hidden";

        //     RegisterButtons(doc);

        //  //   bool isLoginWindow = DetectLoginWindow(e.Uri);

        // //    Task t = new Task(() =>
        // //    {
        // //        System.Threading.Thread.Sleep(1000);
        // //        foreach (IHTMLElement link in doc.getElementsByTagName("a"))
        // //        {
        // //            if (link.className == "sd_email_share default")
        // //            {

        // //            }
        // //            HtmlEventProxy.Create("onclick", link, (a, b) =>
        // //            {
        // //                //if the user navigates between Get Link, Post and e-mail then re-register buttons
        // //                HtmlEventProxy proxy = a as HtmlEventProxy;
        // //                IHTMLAnchorElement input = proxy.HTMLElement as IHTMLAnchorElement;
        // //                RegisterButtons(doc);
        // //            });
        // //        }


        // //        RegisterButtons(doc);
        // //        System.Threading.Thread.Sleep(500);
        // //        DispatcherHelper.CheckBeginInvokeOnUI(() =>
        // //        {
        // //            doc.parentWindow.scrollTo(0, 0);
        // //            if (isLoginWindow)
        // //            {
        // //                doc.parentWindow.scrollBy(0, 30);
        // //            }
        // //            LoginWebBrowser.Visibility = System.Windows.Visibility.Visible;
        // //            BusyBar.Visibility = System.Windows.Visibility.Collapsed;
        // //        });
        // //    });
        // //    t.Start();
        //}

        /// <summary>
        /// Registers click event for buttons on the authorization website.
        /// </summary>
        /// <param name="doc"></param>
        private void RegisterSkyShellExAuthButtons(HTMLDocument doc)
        {
            Task t = new Task(() =>
            {
                System.Threading.Thread.Sleep(500);
                var acceptButton = doc.getElementById("idBtn_Accept");
                var denyButton = doc.getElementById("idBtn_Deny");
                HtmlEventProxy.Create("onclick", (IHTMLElement2)acceptButton, (a, b) =>
                            {
                                LoginWebBrowser.Visibility = System.Windows.Visibility.Hidden;
                                BusyBar.Visibility = System.Windows.Visibility.Visible;
                                _userAccepted = true;

                            });
                HtmlEventProxy.Create("onclick", (IHTMLElement2)denyButton, (a, b) =>
                {
                    App.Current.MainWindow.Close();
                });
            });
            t.Start();
        }



        private bool _authorizationCalled = false;
        void LoginWebBrowser_LoadCompleted(object sender, NavigationEventArgs e)
        {
            if (e.Uri.LocalPath == "/logout.srf") //logout site is loded, nothing todo
                return;

            if (_userAccepted && !_authorizationCalled) //user has accepted the access for SkyShellEx, 
            {
                _viewModel.AccessAccepted.Execute(e.Uri);
                _authorizationCalled = true;
                return;
            }

            HTMLDocument doc = (HTMLDocument)this.LoginWebBrowser.Document;

            try
            {
                Console.WriteLine("disable scrollbars");
                doc.documentElement.style.overflow = "hidden";
            }
            catch { }

            bool isLoginWindow = DetectLoginWindow(e.Uri);
            bool isAuthWindow = DetectAuthWindow(e.Uri);
            bool isSharePage = e.Uri.AbsoluteUri == _viewModel.ShareLink;
            Task t = new Task(() =>
                {
                    if (isAuthWindow) //we are in the authorization window
                    {
                        DispatcherHelper.CheckBeginInvokeOnUI(() =>
                          {
                              double height = doc.body.offsetHeight; //lets get the height off the site
                              double topOffset = (height - App.Current.MainWindow.Height) / 2;
                              App.Current.MainWindow.Top = App.Current.MainWindow.Top - topOffset;
                              App.Current.MainWindow.Height = height; //adjust the window height to the site height
                              doc.parentWindow.scrollTo(0, 70);
                              LoginWebBrowser.Visibility = System.Windows.Visibility.Visible;
                              BusyBar.Visibility = System.Windows.Visibility.Collapsed;
                          });
                        RegisterSkyShellExAuthButtons(doc);
                        return;
                    }
                    if (isLoginWindow) //we are in the login window in case the user isn't signed in to Live
                    {
                        System.Threading.Thread.Sleep(500);
                        DispatcherHelper.CheckBeginInvokeOnUI(() =>
                        {
                            double height = doc.body.offsetHeight; //lets get the height off the site
                            double topOffset = (height - App.Current.MainWindow.Height) / 2;
                            App.Current.MainWindow.Top = App.Current.MainWindow.Top - topOffset;
                            App.Current.MainWindow.Height = height; //adjust the window height to the site height
                            //doc.parentWindow.scrollTo(0, 0);
                            //doc.parentWindow.scrollBy(0, 30);
                            LoginWebBrowser.Visibility = System.Windows.Visibility.Visible;
                            BusyBar.Visibility = System.Windows.Visibility.Collapsed;
                        });
                    }
                    if (isSharePage) //we ar now on the actual sharing page
                    {
                        System.Threading.Thread.Sleep(1000);
                        DispatcherHelper.CheckBeginInvokeOnUI(() =>
                        {
                            try
                            {
                                Console.WriteLine("disable scrollbars");
                                doc.documentElement.style.overflow = "hidden";
                            }
                            catch { }
                           // App.Current.MainWindow.Width = 850;
                            App.Current.MainWindow.Height = 450;
                            LoginWebBrowser.Visibility = System.Windows.Visibility.Visible;
                            BusyBar.Visibility = System.Windows.Visibility.Collapsed;
                        });
                        HtmlEventProxy.Create("onclick", doc, (a, b) => //any click on the site re-registers the button
                        {
                            RegisterButtons(doc);
                        });
                        RegisterButtons(doc); //register all vissible buttons
                        return;
                    }

                });
            t.Start();
        }

        private List<HtmlEventProxy> _shareFolderPageProxies = new List<HtmlEventProxy>();
        private volatile bool _isRegistering = false;

        /// <summary>
        /// Registers input and button clicks we need to handle
        /// </summary>
        /// <param name="doc"></param>
        private void RegisterButtons(HTMLDocument doc)
        {
            if (_isRegistering)
                return;

            try
            {
                // Task t = new Task(() =>
                //{
                foreach (var item in _shareFolderPageProxies)
                {
                    item.Detach();
                }
                _shareFolderPageProxies.Clear();

                var inputCollection = doc.getElementsByTagName("input"); //get all the input items
                var buttonCollection = doc.getElementsByTagName("button"); //get all the buttons

                if (inputCollection != null) //process input items
                {
                    foreach (IHTMLInputElement item in inputCollection)
                    {
                        string aasasd = ((IHTMLElement)item).className;

                        // string t = item.type;
                        if (item.type == "button") //inout item of type button
                        {
                            _shareFolderPageProxies.Add(HtmlEventProxy.Create("onclick", (IHTMLElement2)item, (a, b) =>
                              {
                                  HtmlEventProxy proxy = a as HtmlEventProxy;
                                  IHTMLInputElement input = proxy.HTMLElement as IHTMLInputElement;
                                  IHTMLElement element = proxy.HTMLElement as IHTMLElement;
                                  if (element.className != null && (element.className.ToLower().StartsWith("sd_email_share")
                                      || element.className.ToLower().StartsWith("sd_post_share"))

                                      ) //we now sharing a link via e-mail or post to social network
                                  {
                                      DispatcherHelper.CheckBeginInvokeOnUI(() =>
                                      {
                                          textBlockStatus.Text = "sharing your stuff...";
                                          LoginWebBrowser.Visibility = System.Windows.Visibility.Collapsed;
                                          BusyBar.Visibility = System.Windows.Visibility.Visible;
                                          Task exit = new Task(() =>
                                              {
                                                  System.Threading.Thread.Sleep(3000); //give the website soem time for submitting before we close the app
                                                  DispatcherHelper.CheckBeginInvokeOnUI(() =>
                                                 {
                                                     App.Current.MainWindow.Close();
                                                 });
                                              });
                                          exit.Start();
                                      });
                                  }
                                  else if (element.className.ToLower().StartsWith("sd_cancel") || element.className.ToLower().StartsWith("sd_done")) //user clickes cancel or done
                                  {
                                      //we done
                                      App.Current.MainWindow.Close();
                                  }
                                  else
                                  {
                                      //we don't know what todo, lets do nothing
                                      //MessageBox.Show(element.className);
                                  }
                              })
                              );

                        }
                    }
                }
                if (buttonCollection != null) //process buttons, they appear on the confirmation site for folders that get automatic uploads like Documents or Camera Roll
                {
                    foreach (IHTMLButtonElement item in buttonCollection)
                    {
                        _shareFolderPageProxies.Add(HtmlEventProxy.Create("onclick", (IHTMLElement2)item, (a, b) =>
                        {
                            HtmlEventProxy proxy = a as HtmlEventProxy;
                            IHTMLButtonElement button = proxy.HTMLElement as IHTMLButtonElement;

                            if (button.name == "action") //user clicked the not share button
                            {
                                DispatcherHelper.CheckBeginInvokeOnUI(() =>
                                {
                                    LoginWebBrowser.Visibility = System.Windows.Visibility.Collapsed;
                                    BusyBar.Visibility = System.Windows.Visibility.Visible;
                                    Task exit = new Task(() =>
                                    {
                                        DispatcherHelper.CheckBeginInvokeOnUI(() =>
                                        {
                                           App.Current.MainWindow.Close();
                                        });
                                    });
                                    exit.Start();

                                });

                            }
                            else //user still wants to share the folder
                            {
                                LoginWebBrowser.Visibility = System.Windows.Visibility.Collapsed;
                                BusyBar.Visibility = System.Windows.Visibility.Visible;
                                Task register = new Task(() =>
                                {
                                    System.Threading.Thread.Sleep(2500);
                                    DispatcherHelper.CheckBeginInvokeOnUI(() =>
                                        {
                                            try
                                            {
                                                Console.WriteLine("disable scrollbars");
                                                doc.documentElement.style.overflow = "hidden";
                                            }
                                            catch { }
                                            LoginWebBrowser.Visibility = System.Windows.Visibility.Visible;
                                            BusyBar.Visibility = System.Windows.Visibility.Collapsed;
                                        });
                                    RegisterButtons(doc);
                                });
                                register.Start();
                                //MessageBox.Show(element.className);
                            }
                        })
                          );

                    }
                }
                //});
                // t.Start();
            }
            finally
            {
                _isRegistering = false;
            }
        }

    }

    public class HtmlEventProxy : IDisposable, IReflect
    {
        // Fields
        private EventHandler eventHandler;
        private object sender;
        private IReflect typeIReflectImplementation;
        private IHTMLElement2 htmlElement = null;
        private HTMLDocument _doc = null;
        private string eventName = null;

        // private CTOR
        private HtmlEventProxy(string eventName, IHTMLElement2 htmlElement, EventHandler eventHandler)
        {
            this.eventName = eventName;
            this.htmlElement = htmlElement;
            this.sender = this;
            this.eventHandler = eventHandler;
            Type type = typeof(HtmlEventProxy);
            this.typeIReflectImplementation = type;
        }

        private HtmlEventProxy(string eventName, HTMLDocument doc, EventHandler eventHandler)
        {
            this.eventName = eventName;
            //this.htmlElement = htmlElement;
            _doc = doc;
            this.sender = this;
            this.eventHandler = eventHandler;
            Type type = typeof(HtmlEventProxy);
            this.typeIReflectImplementation = type;
        }

        public static HtmlEventProxy Create(string eventName, IHTMLElement2 htmlElement, EventHandler eventHandler)
        {
            IHTMLElement2 elem = (IHTMLElement2)htmlElement;

            HtmlEventProxy newProxy = new HtmlEventProxy(eventName, elem, eventHandler);
            elem.attachEvent(eventName, newProxy);
            return newProxy;
        }

        public static HtmlEventProxy Create(string eventName, HTMLDocument doc, EventHandler eventHandler)
        {
            //IHTMLElement2 elem = (IHTMLElement2)htmlElement;

            HtmlEventProxy newProxy = new HtmlEventProxy(eventName, doc, eventHandler);
            doc.attachEvent(eventName, newProxy);
            return newProxy;
        }

        /// <summary>
        /// detach only once (thread safe)
        /// </summary>
        public void Detach()
        {
            lock (this)
            {
                if (this.htmlElement != null)
                {
                    IHTMLElement2 elem = (IHTMLElement2)htmlElement;
                    elem.detachEvent(this.eventName, this);
                    this.htmlElement = null;
                }
            }
        }

        /// <summary>
        /// HtmlElemet property  
        /// </summary>
        public IHTMLElement2 HTMLElement
        {
            get
            {
                return this.htmlElement;
            }
        }

        #region IReflect

        FieldInfo IReflect.GetField(string name, BindingFlags bindingAttr)
        {
            return this.typeIReflectImplementation.GetField(name, bindingAttr);
        }

        FieldInfo[] IReflect.GetFields(BindingFlags bindingAttr)
        {
            return this.typeIReflectImplementation.GetFields(bindingAttr);
        }

        MemberInfo[] IReflect.GetMember(string name, BindingFlags bindingAttr)
        {
            return this.typeIReflectImplementation.GetMember(name, bindingAttr);
        }

        MemberInfo[] IReflect.GetMembers(BindingFlags bindingAttr)
        {
            return this.typeIReflectImplementation.GetMembers(bindingAttr);
        }

        MethodInfo IReflect.GetMethod(string name, BindingFlags bindingAttr)
        {
            return this.typeIReflectImplementation.GetMethod(name, bindingAttr);
        }

        MethodInfo IReflect.GetMethod(string name, BindingFlags bindingAttr, Binder binder, Type[] types, ParameterModifier[] modifiers)
        {
            return this.typeIReflectImplementation.GetMethod(name, bindingAttr, binder, types, modifiers);
        }

        MethodInfo[] IReflect.GetMethods(BindingFlags bindingAttr)
        {
            return this.typeIReflectImplementation.GetMethods(bindingAttr);
        }

        PropertyInfo[] IReflect.GetProperties(BindingFlags bindingAttr)
        {
            return this.typeIReflectImplementation.GetProperties(bindingAttr);
        }

        PropertyInfo IReflect.GetProperty(string name, BindingFlags bindingAttr)
        {
            return this.typeIReflectImplementation.GetProperty(name, bindingAttr);
        }

        PropertyInfo IReflect.GetProperty(string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers)
        {
            return this.typeIReflectImplementation.GetProperty(name, bindingAttr, binder, returnType, types, modifiers);
        }

        object IReflect.InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters)
        {
            if (name == "[DISPID=0]")
            {
                if (this.eventHandler != null)
                {
                    this.eventHandler(this.sender, EventArgs.Empty);
                }
            }

            return null;
        }



        Type IReflect.UnderlyingSystemType
        {
            get
            {
                return this.typeIReflectImplementation.UnderlyingSystemType;
            }
        }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            Detach();
        }

        #endregion
    }
}

